import socket
import datetime as dt
import sys
import json
import struct


def load_config(file):
    with open(file, 'r') as fp:
        return json.loads(fp.read())


def get_time():
    return dt.datetime.now()


def duration(time1, time2):
    return (time1 - time2).seconds


def show_time(time):
    return time.time().isoformat()


def show_bytes(data):
    return " ".join([hex(d) for d in data])


def print_info():
    print(f"#"*60)
    print(f"#"*21 + "  Multicast Test  " + f"#"*21)
    print(f"#"*60)
    print(f"# Bind IP: \t\t{bind_ip}")
    print(f"# Bind Port: \t{bind_port}")
    print(f"# Group IP: \t{group_ip}")
    print(f"# Group Port: \t{group_port}")
    print(f"#"*60)
    print(f"# Test Count: \t{count}")
    print(f"# Timeout: \t\t{timeout}")
    print(f"#"*60)
    print(f"# Send Data: \t{show_bytes(send_data)}")
    print(f"#"*60)
    print(f"")


def get_receiver():
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
    sock.bind((bind_ip, bind_port))

    mreq = struct.pack("=4sl", socket.inet_aton(group_ip), socket.INADDR_ANY)
    sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

    sock.settimeout(timeout)
    return sock


if __name__ == '__main__':
    config = load_config("config.json")
    group_ip = config["group_ip"]
    group_port = config["group_port"]
    bind_ip = config["bind_ip"]
    bind_port = config["bind_port"]
    count = config["count"]
    timeout = config["timeout"]
    send_data = bytes.fromhex("".join(config["send_data"].split('\\x')))
    
    print_info()
    receiver = get_receiver()
    
    while count > 0:
        count -= 1
        
        start_at = get_time()
        print(f">> [{show_time(start_at)}] [SEND]")
        receiver.sendto(send_data, (group_ip, group_port))
    
        while duration(get_time(), start_at) < timeout:
            try:
                res = receiver.recvfrom(1024)
                print(f"<< [{show_time(get_time())}] [RECEIVE] [{show_bytes(res[0])}] {res[1]}")
            except socket.timeout:
                print(f"-- [{show_time(get_time())}] [TIMEOUT]")
                print(f"")
            except socket.error as exc:
                print(f"-- [{show_time(get_time())}] [Exception] [{exc}]", file=sys.stderr)
